#include <QCoreApplication>
#include "DBInterface/dbinterfaceais.h"
#include <QMutex>
#include <QDebug>
#include <QSqlQuery>
#include <QSqlError>
#include "Fusion/DBSCAN.h"
#include "Geography/geography.h"
#include "Geography/berthGrid.h"

#define EPS_METER 200
#define MIN_POINTS 1

quint64 getObjectIDFromCoord(qint32 lon,qint32 lat);
QList <SpatialObjectBase*> getSpatialObjectsBase(QSqlQuery query);
void outPutResults(QSqlQuery query, QList<QList<SpatialObjectBase *> > listClusters);

void doCluster(QMutex *mutex, StructDBInfo dbInfo,  DBSCAN &dbscan);

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QMutex *mut=new QMutex();
    StructDBInfo dbinfo;
    dbinfo.connectionName="ConnectionFromDBSCAN";
    dbinfo.dbName="ports";
    dbinfo.hostIP="127.0.0.1";
    dbinfo.pwd=argv[2];
    dbinfo.type="QMYSQL";
    dbinfo.userName=argv[1];
    qDebug()<<"Begin to initiate DBSCAN grids....";

    DBSCAN dbscan;
    qDebug()<<QDateTime::currentDateTime()<<" Begin to query data....";
    doCluster(mut,dbinfo,dbscan);

    return a.exec();
}

void doCluster(QMutex *mutex, StructDBInfo dbInfo, DBSCAN &dbscan)
{
    DBInterfaceAIS *dbI=new DBInterfaceAIS(mutex,dbInfo);
    if(!dbI->connectToDB())
    {
        qDebug()<<"Fail to connect to database. Exit.";
        return;
    }

    QSqlQuery query(*(dbI->db));
    query.setForwardOnly(true);

    QList <SpatialObjectBase*> listSOB=getSpatialObjectsBase(query);
    qDebug()<<QDateTime::currentDateTime()<<" Begin to cluster....";
    QList <QList<SpatialObjectBase*> > listClusters=dbscan.clusterSpatialObjectBase
            (listSOB,EPS_METER,MIN_POINTS);
    qDebug()<<QDateTime::currentDateTime()<<"Clustering complete! Begin to update Table...";

    dbI->startTransaction();
    outPutResults(query,listClusters);
    if(!dbI->commitTransaction())
    {
        qDebug()<<"Fail to commit transaction:"<<dbI->db->lastError().text();
    }
    else
        qDebug()<<"Database updated successfully";

    while(!listSOB.isEmpty())
    {
        delete listSOB.first();
        listSOB.removeFirst();
    }

    qDebug()<<QDateTime::currentDateTime()<<"Process complete!";
}

QList <SpatialObjectBase*> getSpatialObjectsBase(QSqlQuery query)
{
    QList <SpatialObjectBase*> listSpatialObjectBase;
    if(!query.exec("select Upper_Left_Longitude, "
              "Upper_Left_Latitude,Avg_Heading from l0_mooring_since201507"
               " where Membership_Mooring>=20"))
    {
        qDebug()<<"Fail to select from table:"<<query.lastError();
        return listSpatialObjectBase;
    }
    while(query.next())
    {
        int lon=query.value(0).toInt();
        int lat=query.value(1).toInt();
        int heading=query.value(2).toInt();
        quint64 id=getObjectIDFromCoord(lon,lat);
        BerthGrid *spatialObj=new BerthGrid(id,lon/COORD_DIVIDER,lat/COORD_DIVIDER,heading);
        listSpatialObjectBase.append(spatialObj);
    }
    qDebug()<<"Number of spatialobjects:"<<listSpatialObjectBase.size();
    return listSpatialObjectBase;
}

void outPutResults(QSqlQuery query,QList <QList<SpatialObjectBase*> > listClusters)
{
    qDebug()<<"Number of clusters:"<<listClusters.size();
    QListIterator <QList<SpatialObjectBase*> > iteCluster(listClusters);
    quint32 clusterID=0;
    if(!query.prepare("update l0_mooring_since201507 set ClusterID=:clusterID "
                  " where Upper_Left_Longitude=:Upper_Left_Longitude and "
                  " Upper_Left_Latitude=:Upper_Left_Latitude"))
        qDebug()<<"Fail to prepare sql:"<<query.lastError().text();
    while(iteCluster.hasNext())
    {
        QList<SpatialObjectBase*> listSOBJs = iteCluster.next();
        QListIterator <SpatialObjectBase* > iteOBJ(listSOBJs);
        query.bindValue(":clusterID",clusterID);
        while(iteOBJ.hasNext())
        {
            SpatialObjectBase* sob=iteOBJ.next();
            qint32 lat =qRound(sob->getLatitude()*COORD_DIVIDER);
            qint32 lon =qRound(sob->getLongitude()*COORD_DIVIDER);
            query.bindValue(":Upper_Left_Latitude",lat);
            query.bindValue(":Upper_Left_Longitude",lon);
            if(!query.exec())
                qDebug()<<"Fail to exec sql:"<<query.lastError().text();

        }
        clusterID++;
    }
}


quint64 getObjectIDFromCoord(qint32 lon,qint32 lat)
{
    quint64 offSet=181*600000;
    return ((lon+offSet)<<32)+lat+offSet;
}
